[DRAFT] Initial work on allowing Model to output to Data#12
[DRAFT] Initial work on allowing Model to output to Data#12iKenndac wants to merge 19 commits intotomasf:devfrom
Conversation
Removed Windows SDK component from setup step.
Added an example code snippet and updated Cadova version in README.
Updated image width in README example.
Adjusted image width in README example.
Updated the list of dependencies in the README. [no ci]
|
Nice. This makes sense. The main concern is the breaking change. The way the There are a few backwards-compatible options I can think of:
What do you think? |
|
I'd personally argue that a What we could do is keep the existing API and initialiser, but mark it as deprecated. This won't break existing projects, and will nudge users towards using a new initialiser then either So it'd be something like: await Model("MyCoolModel") { … } // WARNING: Deprecated. Use Model(named:) and .writeToDisk() or .generateData() instead.
let url = await Model(named: "MyCoolModel") { … }
.writeToDisk()
let fileContents = await Model(named: "MyCoolModel") { … }
.generateData()This keeps backward compatibility and moves over to giving the client choice of what happens with very minimal impact on the rest of the project. What do you think? |
|
In the vast majority of cases, writing to disk is what people need. It's the current workflow for previewing while developing a model, and it's how you get something to give a slicer to print. I designed this use case to be as lightweight as possible – just a simple call to Model with a name and the geometry in a result builder. Getting the model as data is awesome, but I'm not convinced adding boilerplate to the common case to enable it is the way to go. |
|
Ok, how about this: A new, optional parameter to the initialiser, something like So: await Model("MyCoolModel") { … } // Same behaviour and API as now
let fileContents: Data = await Model("MyCoolModel", automaticallyWritesToDisk: false) { … }
.generateData()
let url: URL = await Model("MyCoolModel", automaticallyWritesToDisk: false) { … }
.writeToDisk()The keeps the existing behaviour as the default, and adds options for those who want more control. What do you think? |
|
This looks like a good solution! 👍 |
|
Alright, I've made some changes. I'm thinking about three use cases: Question: I saw that the
Model("MyCoolModel") { … } // Will write to the current working directoryFor folks wanting more control for whatever reason, I added the method let directoryUrl: URL = …
// NOTE: This will always return `nil` if the file already exists?
let fileUrl: URL? = Model("MyCoolModel", automaticallyWriteToDisk: false) { … }
.writeToDirectory(directoryUrl)
let fileUrl: URL = … // Present a system save dialog etc
let fileName: String = fileUrl.lastPathComponent.deletingPathExtension
try Model(fileName, automaticallyWriteToDisk: false) { … }
.writeToFile(fileUrl)
let file: InMemoryFile? = try Model("MyCoolModel", automaticallyWriteToDisk: false) { … }
.generateData()
let fileName: String = file?.suggestedFileName
let fileContents: Data = file?.contentsBoth Let me know what you think. If you're happy with the code changes, I'll update all the documentation etc. |
|
I also realised I probably should've pointed this PR at |
The reason is tied to Cadova’s reveal workflow. New models are automatically revealed in the file browser only when they don’t already exist, which makes it easy to immediately open them in Cadova Viewer (or another viewer). On subsequent updates, the expectation is that the viewer reloads the existing file and we don't want to interrupt the user by revealing again. When generating via a This is also why I’ve been a bit cautious about reusing Your code looks solid. I’m still wondering whether stretching |
|
I don't really think that Ignoring preexisting API, I think a sensible design would be:
At any rate, I generally really don't think that one extra line to instruct a model to write to disk etc is enough to be considered boilerplate. Since you want to keep the existing API, that's a bit more complex since before this PR, the thing called Here's my proposal for a middle-ground:
enum DirectoryWriteResult {
case newFileWritten(URL)
case existingFileOverwritten(URL)
}
Have a think and let me know your decision. I'm happy to build this out and do all the docs etc once a concrete decision is made, and I'd love to be able to use this in my projects. |
|
I think your approach is a reasonable way to structure this, and if Cadova were primarily a library-first project I’d probably lean more in that direction. That said, for Cadova as it exists today, I want to keep Since Cadova is first and foremost intended as a replacement for tools like OpenSCAD, I tend to see embedded usage as more of a power-user or advanced scenario. With that in mind, separating it into its own API feels like a reasonable trade-off to me. Model’s documentation could make it explicit that it’s primarily aimed at "CLI-style" usage and then point to the separate API for embedded use cases. A short wiki page or guide could also help with discoverability. I’ve done a small refactor of the What I have is not meant as a concrete proposal — naming, documentation, and likely some functionality are still missing — but rather a demonstration of the general shape of the approach I’m referring to. In this sketch, |
|
Hi! Just wanted to say that I haven't forgotten this, I've just been super busy this week. I'll pick this back up at the weekend. |
|
Closing this PR as it's been moved to #18. |
This PR relates to issue #11.
This is early work on allowing
Modelto output to an in-memory representation rather than always to disk.Since
Model's initialiser was very side-effecty (creating an instance ofModelcreated a file on disk), this is a breaking API change. I've added two public functions:generateInMemoryFile(…) -> InMemoryFile?returns the newInMemoryFilestruct, which contains file data and a path extension.writeToFile(…) -> URL?writes to disk as before, and returns the URL to that file.I'd like feedback on this before I go further, since this breaks all existing usages of
Modeland needs a ton of documentation updates (wiki, examples, etc). I also think this change makes theisCollectingModelsflag obsolete, but I'm not super familiar with the project.Another thought I had would be to add free functions that mimic the old API, as this project does with the
Projecttype.Let me know how you feel about the proposed changes, and I'll be happy to adapt and move forward.